如何客製化 ASP.NET Core 的 Model Validation 預設錯誤訊息
TLDR
- ASP.NET Core 預設的 Model Validation 僅提供英文訊息,可透過資源檔(.resx)與
IValidationMetadataProvider進行客製化。 - 驗證訊息分為「ModelBinding」與「ValidationMetadata」兩部分,需分別實作設定。
- 透過
ModelBindingMessageProvider可直接覆寫 ModelBinding 錯誤訊息。 - 透過實作
IValidationMetadataProvider介面,可動態替換ValidationAttribute的預設錯誤訊息。 - 若需支援多國語系,可建立對應語系的資源檔,並透過
RequestLocalizationOptions設定SupportedUICultures來自動切換。 - 設定語系時應區分
Culture(格式設定)與UICulture(資源檔載入),錯誤訊息的顯示取決於UICulture。
處理 ModelBinding 與 ValidationMetadata 驗證訊息
在 ASP.NET Core 中,Model Validation 的錯誤訊息來源分為兩類,處理方式如下:
1. 建立資源檔 (.resx)
建立資源檔以存放自定義的錯誤訊息,屬性設定為「內嵌資源」,並根據需求設定存取修飾詞。
- ModelBindingMessage:處理資料格式相關錯誤(如型別轉換失敗)。
- ValidationMetadataMessage:處理資料內容驗證錯誤(如
Required、Range等屬性)。
2. 客製化 ValidationMetadataProvider
若要替換 ValidationAttribute 的預設錯誤訊息,需實作 IValidationMetadataProvider。
什麼情況下會遇到這個問題:當您希望統一修改專案中所有 ValidationAttribute(如 RequiredAttribute)的預設英文錯誤訊息,而不需在每個屬性上重複撰寫 ErrorMessage 時。
csharp
public class LocalizationValidationMetadataProvider : IValidationMetadataProvider {
private readonly ResourceManager resourceManager;
private readonly Type resourceType;
public LocalizationValidationMetadataProvider(Type type) {
resourceType = type;
resourceManager = new ResourceManager(type);
}
public void CreateValidationMetadata(ValidationMetadataProviderContext context) {
foreach (var attribute in context.ValidationMetadata.ValidatorMetadata.OfType<ValidationAttribute>()) {
if (attribute.ErrorMessageResourceName is null) {
bool hasErrorMessage = attribute.ErrorMessage != null;
if (hasErrorMessage) {
string? defaultErrorMessage = typeof(ValidationAttribute)
.GetField("_defaultErrorMessage", BindingFlags.NonPublic | BindingFlags.Instance)
?.GetValue(attribute) as string;
hasErrorMessage = attribute.ErrorMessage != defaultErrorMessage;
}
if (hasErrorMessage) {
continue;
}
string? name = GetMessageName(attribute);
if (name != null && resourceManager.GetString(name) != null) {
attribute.ErrorMessageResourceType = resourceType;
attribute.ErrorMessageResourceName = name;
attribute.ErrorMessage = null;
}
}
}
}
private string? GetMessageName(ValidationAttribute attr) {
switch (attr) {
case CompareAttribute _:
return "CompareAttribute_MustMatch";
case StringLengthAttribute vAttr:
if (vAttr.MinimumLength > 0) {
return "StringLengthAttribute_ValidationErrorIncludingMinimum";
}
return "StringLengthAttribute_ValidationError";
case DataTypeAttribute _:
return $"{attr.GetType().Name}_Invalid";
case ValidationAttribute _:
return $"{attr.GetType().Name}_ValidationError";
}
return null;
}
}3. 註冊服務
在 Program.cs 中註冊上述 Provider 並設定 ModelBindingMessageProvider。
csharp
builder.Services.AddRazorPages()
.AddMvcOptions(options => {
var provider = options.ModelBindingMessageProvider;
provider.SetAttemptedValueIsInvalidAccessor((x, y) => string.Format(ModelBindingMessage.AttemptedValueIsInvalid, x, y));
provider.SetMissingBindRequiredValueAccessor(x => string.Format(ModelBindingMessage.MissingBindRequiredValue, x));
// ... 其他 ModelBinding 訊息設定
options.ModelMetadataDetailsProviders.Add(new LocalizationValidationMetadataProvider(typeof(ValidationMetadataMessage)));
});實作多國語系支援
什麼情況下會遇到這個問題:當應用程式需要根據使用者的語系設定,動態顯示對應語言的驗證錯誤訊息時。
設定步驟
- 建立對應語系的資源檔,如
ModelBindingMessage.zh-TW.resx。 - 資源檔屬性設定為「內嵌資源」,且「不要產生程式碼」。
- 在
Program.cs中配置RequestLocalizationOptions。
csharp
WebApplication app = builder.Build();
string[] supportedCultures = new string[] { "zh-TW", "en-US" };
RequestLocalizationOptions localizationOptions = new RequestLocalizationOptions()
.SetDefaultCulture(supportedCultures[0])
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures);
app.UseRequestLocalization(localizationOptions);INFO
Culture 與 UICulture 的區別
- Culture:決定日期、數值、貨幣的格式與排序規則。
- UICulture:決定載入何種語系的資源檔(錯誤訊息顯示依賴此設定)。
- 若使用 QueryString 傳遞語系,正確參數應為
ui-culture而非culture。
異動歷程
- 初版文件建立。
- 修正
ModelBindingMessage的訊息。
- 修正